import arrow

from typing import Tuple

from bson import ObjectId

from models.task import ScanTaskSessions, ScanEvents, ScanTaskSessionsExtra
from models.target import Targets

from apps.session.serializer import SerializerSessionInfo
from apps.notify.failed import get_stopped_reason

from utilities.meta_view import Response
from utilities.task_config import PARSER_MAP
from utilities.scanner import GetPagesUrlList
from utilities.utils import EventParser
from utilities.enums import EXTaskType

from apps.target.views import (
    TargetAppendix,
    TargetListEvent,
    TargetSummary,
    TargetSiteInfo,
)
from apps.task.views import ListScanTask


class SessionSummary(TargetSummary):
    def retrieve(self):
        task_session_id = self.url_args.get('id')

        query = {'task_session_id': task_session_id}

        session = ScanTaskSessions.objects.find_one(query)
        events = tuple(
            ScanEvents.objects.aggregate(
                [
                    {'$match': {'task_session_id': task_session_id}},
                    {'$project': {'_id': 0}},
                ]
            )
        )

        task_type = session.addition.get('task_type')
        event_parser = PARSER_MAP.get(task_type)()
        event_parser.set_events(events)

        e_parser = EventParser([event_parser])
        e_parser.set_option(use_risk_detail=True)
        parser_data = e_parser.get()

        summary = {
            'score': parser_data.get('score'),
            **parser_data.get('task_type_severity_map'),
        }
        url_data = GetPagesUrlList(task_session_id=session.task_session_id).get()

        if summary.get('score') > 100:
            summary['score'] = 100

        serializer = SerializerSessionInfo(session)
        severity_map = self.get_severity_map()

        task_type_status_maps = {
            'availability': 'ok',
            'availability_cloud': 'ok',
            'securityEvent': 'ok',
            'vul': 'ok',
            'risk_link': 'ok',
            'target_info': 'ok',
        }

        res = {
            'target_url': session.target_url,
            'info': serializer.data,
            'total_url': url_data.get('count', 0),
            'summary': summary,
            'task_events': {task_type: events},
            'severity_map': severity_map,
            'risk_detail': parser_data.get('risk_detail'),
            'risk_distribution': parser_data.get('task_type_severity_map'),
            'task_type_status_maps': task_type_status_maps,
        }
        return Response(res)


class SessionAppendix(TargetAppendix):
    def get_target_and_session(self):
        task_session_id = self.url_args.get('id')
        query = {'task_session_id': task_session_id}

        obj_session = ScanTaskSessions.objects.find_one(query)
        extra = ScanTaskSessionsExtra.objects.filter(
            task_session_id=task_session_id
        ).first()
        session = obj_session.to_dict()
        extra = extra.to_dict() if extra else {}
        extra.pop('_id', None)
        session['extra'] = extra
        sessions = [session]

        obj_target = Targets.objects.find_one({'target_id': obj_session.target_id})

        return obj_target, sessions, obj_session.addition.get('task_type')


class SessionListEvent(TargetListEvent):
    def get_session(self) -> Tuple[ScanTaskSessions, str]:
        task_session_id = self.url_args.get('id')

        query = {'task_session_id': task_session_id}

        obj_session = (
            ScanTaskSessions.objects.find(query).order_by('-updated_at').first()
        )

        return obj_session, obj_session.addition.get('task_type')


class SessionSiteInfo(TargetSiteInfo):
    def retrieve(self):
        task_session_id = self.url_args.get('id')

        query = {'task_session_id': task_session_id}

        pipeline = [
            {'$match': query},
            {
                '$lookup': {
                    'from': getattr(ScanEvents, '_meta').get('collection'),
                    'localField': 'task_session_id',
                    'foreignField': 'task_session_id',
                    'as': 'event',
                }
            },
            {'$unwind': '$event'},
            {'$project': {'detail': '$event.detail'}},
        ]

        session = tuple(ScanTaskSessions.objects.aggregate(pipeline))[0]

        return Response({'detail': session.get('detail', {})})


class SessionList(ListScanTask):
    def list(self):
        page, limit = self.get_paginate()
        task_type = self.request.GET.get('task_type')

        targets_id_map = self.get_target_id_map()

        task_session_query = {
            'target_id': {'$in': list(targets_id_map.keys())},
            'addition.task_type': {'$ne': EXTaskType.target_info.value},
            'status': {'$in': [8, 9]},
        }

        if task_type:
            task_session_query['addition.task_type'] = task_type

        all_sessions = (
            ScanTaskSessions.objects.find(task_session_query)
            .order_by('-updated_at')
            .paginate(limit)
        )
        task_sessions = all_sessions.page(page)

        data = []
        task_session_id_list = []
        for obj_session in task_sessions:
            task_id = obj_session.task_id
            task_session_id = obj_session.task_session_id
            target = targets_id_map.get(obj_session.target_id, {})
            reason = ''
            if obj_session.status == 8:
                reason = get_stopped_reason(obj_session.task_config)
            temp_dict = {
                'task_id': task_id,
                'target_url': target.get('target_url', ''),
                'target_name': target.get('target_name', ''),
                'target_title': target.get('target_title', ''),
                'target_group_name': target.get('target_group', {}).get(
                    'target_group_name'
                ),
                'task_type': obj_session.addition.get('task_type', ''),
                'status': obj_session.status,
                'progress': obj_session.progress,
                'is_disabled': obj_session.task_config.get('is_disabled'),
                'task_session_id': task_session_id,
                'updated_at': arrow.get(obj_session.updated_at).isoformat(),
                'reason': reason,
            }
            data.append(temp_dict)
            task_session_id_list.append(task_session_id)

        return Response({'data': data, 'page': page, 'total': all_sessions.count})
